package packet

import (
	"bytes"
	"fmt"
	"sync"
)

// Packet协议定义

/*
	### packet header
		1 byte: commandID
	### submit packet
		8字节 ID 字符串
	任意字节 payload
		### submit ack packet
	8字节 ID 字符串
		1字节 result
*/

// 创建了一个 SubmitPool 变量，它的类型为 sync.Pool，这就是内存对象池，池中的对象都是 Submit
// 在 packet.Decode 中收到 Submit 类型请求时，也不需要新分配一个 Submit 对象，而是直接从 SubmitPool 代表的 Pool 池中取出一个复用
var SubmitPool = sync.Pool{
	New: func() interface{} {
		return &SubmitRequest{}
	},
}

const (
	CommandConnection    = iota + 0x01 //  0x01
	CommandSubmitRequest               //  0x02
)

const (
	CommandConnectionAck    = iota + 0x21 //  0x21
	CommandSubmitRequestAck               //  0x22
)

// 定义 packet编解码接口
type Packet interface {
	Encode() ([]byte, error) //  struct -> []byte
	Decode([]byte) error     //  []byte -> struct
}

// 定义提交请求的结构体
type SubmitRequest struct {
	ID      string // commandID
	Payload []byte // 应用层需要的业务数据
}

// SubmitRequest 实现提交请求的编码方法
func (s *SubmitRequest) Encode() ([]byte, error) {
	return bytes.Join([][]byte{[]byte(s.ID[:8]), s.Payload}, nil), nil
}

// SubmitRequest 实现请求的解码方法
func (s *SubmitRequest) Decode(body []byte) error {
	s.ID = string(body[:8])
	s.Payload = body[8:]
	return nil
}

// 定义请求响应的结构体
type SubmitRequestAck struct {
	ID     string // commandID
	Result uint8  // 成功的响应用 0 表示，失败的响应用 1 来表示
}

// SubmitRequestAck 实现请求的编码方法
func (s *SubmitRequestAck) Encode() ([]byte, error) {
	return bytes.Join([][]byte{[]byte(s.ID[:8]), []byte{s.Result}}, nil), nil
}

// SubmitRequestAck 实现请求的解码方法
func (s *SubmitRequestAck) Decode(body []byte) error {
	s.ID = string(body[0:8])
	s.Result = uint8(body[8])
	return nil
}

// 包级的 Encode 函数，根据传入的 packet 类型调用对应的 Encode 方法实现对象的编码
func Encode(packet Packet) ([]byte, error) {
	var commandID uint8
	var body []byte
	var err error
	// x.(type)只能在switch中使用,和switch case配合使用 ,获取到类型
	switch t := packet.(type) {
	case *SubmitRequest:
		commandID = CommandSubmitRequest
		body, err = packet.Encode()
		if err != nil {
			return nil, err
		}
	case *SubmitRequestAck:
		commandID = CommandSubmitRequestAck
		body, err = packet.Encode()
		if err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("unknown type %s", t)
	}
	return bytes.Join([][]byte{[]byte{commandID}, body}, nil), nil
}

// 在包级提供一个导出的函数 Decode，这个函数负责从字节流中解析出对应的类型（根据 commandID），并调用对应类型的 Decode 方法
func Decode(packet []byte) (Packet, error) {
	commandID := packet[0]
	pktBody := packet[1:]

	switch commandID {
	case CommandConnection:
		return nil, nil
	case CommandConnectionAck:
		return nil, nil
	case CommandSubmitRequest:
		// 从SubmitPool池中获取一个Submit内存对象
		s := SubmitPool.Get().(*SubmitRequest)
		err := s.Decode(pktBody)
		if err != nil {
			return nil, err
		}
		return s, nil
	case CommandSubmitRequestAck:
		s := SubmitRequestAck{}
		err := s.Decode(pktBody)
		if err != nil {
			return nil, err
		}
		return &s, nil
	default:
		return nil, fmt.Errorf("unknown commandID [%d]", commandID)
	}
}
